package org.dru.clay.rhino;

import org.dru.clay.rhino.feature.Feature;
import org.dru.clay.rhino.feature.Features;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

import java.io.File;
import java.io.FileReader;
import java.io.PrintStream;

/**
 * Module
 * User: joakimd
 * Date: 2013-07-17
 * Time: 10:21
 */
public final class Module extends Feature {
    private static final String[] FUNCTION_NAMES = {
            "Feature_load",
            "Console_print",
            "Module_load",
            "Script_load"
    };

    private final String name;
    private final File baseDir;

    public Module(final String name, final File baseDir) {
        this.name = name;
        this.baseDir = baseDir;
    }

    @Override
    public void init(final Context context, final ScriptableObject scope) throws Exception {
        // define properties
        scope.defineProperty("_type_", "module", ScriptableObject.CONST);
        scope.defineProperty("name", name, ScriptableObject.CONST);
        scope.defineProperty("baseDir", baseDir.getPath(), ScriptableObject.CONST);
        // define FUNCTION_NAMES
        scope.defineFunctionProperties(FUNCTION_NAMES, Module.class, ScriptableObject.DONTENUM);
        // the feature script.
        loadFeatureScript(context, scope, getClass());
    }

    public static void Feature_load(final Context context, final Scriptable thisObj,
                                    final Object[] args, final Function funcObj) throws Exception {
        final String name = toStringArray(args)[0];
        Features.valueOf(name).get().init(context, (ScriptableObject) thisObj);
    }

    public static void Console_print(final Context context, final Scriptable thisObj,
                                     final Object[] args, final Function funcObj) throws Exception {
        final String[] arguments = toStringArray(args);
        final PrintStream ps;
        if (arguments[0].equals("out")) {
            ps = System.out;
        } else if (arguments[0].equals("err")) {
            ps = System.err;
        } else {
            throw new Error("no such print-stream: " + arguments[0]);
        }
        ps.println(arguments[1]);
    }

    public static Object Module_load(final Context context, final Scriptable thisObj,
                                     final Object[] args, final Function funcObj) throws Exception {
        final String name = toStringArray(args)[0];
        final String baseDir = getString(thisObj, "baseDir");
        final Module module = new Module(name, new File(baseDir, name));
        final ScriptableObject moduleObj = (ScriptableObject) context.newObject(thisObj);
        module.init(context, moduleObj);
        return moduleObj;
    }

    public static void Script_load(final Context context, final Scriptable thisObj,
                                   final Object[] args, final Function funcObj) throws Exception {
        final String[] arguments = toStringArray(args);
        final String baseDir = getString(thisObj, "baseDir");
        loadScriptFile(context, thisObj, new File(baseDir, arguments[0]).getCanonicalFile());
    }

    public static void loadScriptFile(final Context context, final Scriptable scope, final File file) throws Exception {
        final FileReader reader = new FileReader(file);
        try {
            context.evaluateReader(scope, reader, file.getPath(), 1, null);
        } finally {
            reader.close();
        }
    }
}
